package com.jsh.erp.service.invoice;

import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.TypeReference;
import com.jsh.erp.datasource.entities.*;
import com.jsh.erp.datasource.mappers.InvoiceMapper;
import com.jsh.erp.datasource.vo.InvoiceVo;
import com.jsh.erp.exception.BusinessRunTimeException;
import com.jsh.erp.service.depotHead.DepotHeadService;
import com.jsh.erp.service.depotItem.DepotItemService;
import com.jsh.erp.service.invoiceItem.InvoiceItemService;
import com.jsh.erp.service.user.UserService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;
import org.springframework.util.ObjectUtils;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.validation.Valid;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 发票服务类
 *
 * 该服务类用于业务逻辑的处理，包括发票的增、删、改、查操作
 *
 * @Author yik
 * @Date 2025-01-08
 */
@Service
@Slf4j
public class InvoiceService {

    @Resource
    private InvoiceMapper invoiceMapper;
    @Resource
    private InvoiceItemService invoiceItemService;
    @Resource
    private DepotItemService depotItemService;
    @Resource
    private DepotHeadService depotHeadService;

    @Resource
    private UserService userService;


    @Transactional(value = "transactionManager", rollbackFor = Exception.class)
    public int addInvoice(@Valid JSONObject obj, HttpServletRequest request) {
        //1.解析出数据 info->基础票据信息、rows->明细信息
        Map<String, Object> info = obj.getJSONObject("info").toJavaObject(Map.class);
        //discountLastMoney->税后金额、totalAmount->票据存储的税后金额。因为前端直接改成total...会报错（多出地方引用），因此在后端再赋值一次。
        info.put("totalAmount", info.get("discountLastMoney"));
        TypeReference<List<Map<String, Object>>> typeRef = new TypeReference<List<Map<String, Object>>>() {}; //转型用-（泛型）
        List<Map<String, Object>> rows = obj.getJSONArray("rows").toJavaObject(typeRef);
        try {
            //当前登录用户信息
            User user = userService.getCurrentUser();
            Assert.state(! ObjectUtils.isEmpty(user), "获取当前用户信息错误，请退出后重新登录");
            Invoice invoice = JSONObject.parseObject(JSONObject.toJSONString(info), Invoice.class);
            invoice.setCreatedAt(new Date());
            invoice.setInvoiceDate(ObjectUtils.isEmpty(invoice.getInvoiceDate())?new Date():invoice.getInvoiceDate());
            invoice.setCreatedBy(user.getUsername());
            int resultInvoice = invoiceMapper.insertInvoice(invoice);
            Assert.state(resultInvoice!=0,"发票插入失败");

            // 处理明细
            List<InvoiceItem> details = rows.stream()
                    .map(row -> createInvoiceItem(row, invoice.getId()))
                    .collect(Collectors.toList());

            int resultDetails = invoiceItemService.addInvoice(details);
            Assert.state(resultDetails==details.size(),"发票详情数据插入失败");

            // 处理单据详细记录的开票数量
            details.forEach(item->{
                Assert.state(!ObjectUtils.isEmpty(item.getHeaderId()),"明细数据异常");
                try {
                    DepotItem depotItem = depotItemService.getDepotItem(item.getHeaderId());
                    Assert.state(!ObjectUtils.isEmpty(depotItem),"开票数量扣减异常");

                    BigDecimal subVal = depotItem.getInvoiceNumber().add(item.getQuantity());
                    Assert.state(depotItem.getBasicNumber().compareTo(subVal)>=0,"此次开票数量高于原数量");

                    depotItem.setInvoiceNumber(depotItem.getInvoiceNumber().add(item.getQuantity()));
                    int depotNum = depotItemService.updateDepotItemWithObj(depotItem);
                    Assert.state(depotNum>0, "修改单据数量失败！");
                } catch (Exception e) {
                    throw new BusinessRunTimeException(500,e.getMessage());
                }
            });

            //处理单据状态，如果所有商品都已经开票完成那么修改状态为已开票
            String[] split = invoice.getDeliveryNoteId().split(",");
            for (String headerId : split) {
                Long id = Long.valueOf(headerId);
                DepotHead head = depotHeadService.getDepotHead(id);
                Assert.state(!ObjectUtils.isEmpty(head), "单据状态异常");
                BigDecimal count = BigDecimal.ZERO;
                List<DepotItemVo4WithInfoEx> detailList = depotItemService.getDetailList(Long.valueOf(id));
                for (DepotItemVo4WithInfoEx withInfoEx : detailList) {count = count.add(withInfoEx.getInvoiceNumber());}
                head.setInvoiceQuantity(count);
                int resultHead = depotHeadService.updateDepotHead(JSONObject.parseObject(JSONObject.toJSONString(head)), null);
                Assert.state(resultHead>0,"修改单据开票数量出错啦！");
            }

        } catch (Exception e) {
            throw new BusinessRunTimeException(500, e.getMessage());
        }
        return 1;
    }

    private InvoiceItem createInvoiceItem(Map<String, Object> row, Long invoiceId) {
        InvoiceItem item = new InvoiceItem();
        item.setInvoiceId(invoiceId);
        item.setProductName((String)row.get("name"));
        item.setProductCode((String)row.get("barCode"));
        item.setProductModel(((String)row.get("model")).concat((String)row.get("standard")));
        item.setQuantity(new BigDecimal(String.valueOf(ObjectUtils.isEmpty(row.get("operNumber"))?BigDecimal.ZERO:row.get("operNumber"))));
        item.setUnitPrice(new BigDecimal(String.valueOf(ObjectUtils.isEmpty(row.get("unitPrice"))?BigDecimal.ZERO:row.get("unitPrice"))));
        item.setTaxRate(new BigDecimal(String.valueOf(ObjectUtils.isEmpty(row.get("taxRate"))?BigDecimal.ZERO:row.get("taxRate"))));
        item.setTaxAmount(new BigDecimal(String.valueOf(ObjectUtils.isEmpty(row.get("taxMoney"))?BigDecimal.ZERO:row.get("taxMoney"))));
        item.setTotalPrice(new BigDecimal(String.valueOf(ObjectUtils.isEmpty(row.get("taxLastMoney"))?BigDecimal.ZERO:row.get("taxLastMoney"))));
        item.setCreatedAt(new Date());
        item.setRemark((String)row.get("remark"));
        item.setHeaderId(Long.valueOf(String.valueOf(row.get("id"))));
        return item;
    }


    public int updateInvoice(Invoice Invoice) {
        return invoiceMapper.updateInvoice(Invoice);
    }

    public List<InvoiceVo> getAllInvoices(Map<String, String> map) {
        return invoiceMapper.findAllInvoices(map);
    }

    public Long countInvoicesByConditions(Map<String, String> map) {
        return invoiceMapper.countInvoicesByConditions(map);
    }

    public Invoice getInvoiceById(Long id) {
        return invoiceMapper.findInvoiceById(id);
    }

    public int deleteInvoice(Long id) {
        return invoiceMapper.deleteInvoice(id);
    }
}